package com.lagou.sqlSession;


import com.lagou.config.BoundSql;
import com.lagou.pojo.Configuration;
import com.lagou.pojo.MappedStatement;
import com.lagou.utils.GenericTokenParser;
import com.lagou.utils.ParameterMapping;
import com.lagou.utils.ParameterMappingTokenHandler;

import javax.management.IntrospectionException;
import javax.sql.DataSource;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class SimpleExecutor implements Executor {


    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, IntrospectionException, InstantiationException, InvocationTargetException {
        DataSource dataSource = configuration.getDataSource();
        // 获取数据库连接
        Connection connection = dataSource.getConnection();
        PreparedStatement preparedStatement = getPreparedStatement(connection, mappedStatement, params);
        // 执行sql
        ResultSet resultSet = preparedStatement.executeQuery();
        // 封装结果集
        String resultType = mappedStatement.getResultType(); // 获取参数类型
        Class<?> resultTypeClassType = getClassType(resultType);
        // 结果集合
        List<Object> resultObjects = new ArrayList<Object>();

        while (resultSet.next()) {
            Object o = resultTypeClassType.newInstance();
            // 获取数据库表中的元数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                String columnName = metaData.getColumnName(i);
                Object object = resultSet.getObject(columnName);
                // 使用内省，完成数据库字段和实体之前属性的一一映射
                try {
                } catch (Exception e) {
                    PropertyDescriptor propertyDescriptor = null;
                    try {
                        propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClassType);
                    } catch (java.beans.IntrospectionException ex) {
                        ex.printStackTrace();
                    }
                    Method writeMethod = propertyDescriptor.getWriteMethod();// 内省完成之后会生成get/set方法
                    writeMethod.invoke(o, object);
                }


            }
            resultObjects.add(o);
        }
        return (List<E>) resultObjects;
    }

    @Override
    public void insert(Configuration configuration, MappedStatement mappedStatement, Object... params) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, IntrospectionException, InstantiationException, InvocationTargetException {
        DataSource dataSource = configuration.getDataSource();
        // 获取数据库连接
        Connection connection = dataSource.getConnection();
        PreparedStatement preparedStatement = getPreparedStatement(connection, mappedStatement, params);
        // 执行sql
        try {
            preparedStatement.executeUpdate();
            connection.setAutoCommit(false);
        } catch (Exception e) {
            e.printStackTrace();
            connection.rollback();
        }
    }

    @Override
    public void update(Configuration configuration, MappedStatement mappedStatement, Object... params) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, IntrospectionException, InstantiationException, InvocationTargetException {
        DataSource dataSource = configuration.getDataSource();
        // 获取数据库连接
        Connection connection = dataSource.getConnection();
        PreparedStatement preparedStatement = getPreparedStatement(connection, mappedStatement, params);
        // 执行sql
        try {
            preparedStatement.executeUpdate();
            connection.setAutoCommit(false);
        } catch (Exception e) {
            e.printStackTrace();
            connection.rollback();
        }

    }

    @Override
    public void delete(Configuration configuration, MappedStatement mappedStatement, Object... params) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, IntrospectionException, InstantiationException, InvocationTargetException {
        DataSource dataSource = configuration.getDataSource();
        // 获取数据库连接
        Connection connection = dataSource.getConnection();
        PreparedStatement preparedStatement = getPreparedStatement(connection, mappedStatement, params);
        // 执行sql
        try {
            preparedStatement.executeUpdate();
            connection.setAutoCommit(false);
        } catch (Exception e) {
            e.printStackTrace();
            connection.rollback();
        }
    }


    private Class<?> getClassType(String paramterType) throws ClassNotFoundException {
        if (paramterType != null) {
            Class<?> aClass = Class.forName(paramterType);
            return aClass;
        }
        return null;

    }


    /**
     * 解析sql语句，进行占位转化
     *
     * @param sql
     * @return
     */
    private BoundSql getBoundedSql(String sql) {
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        // 解析完成的sql，进行?占位,同时参数对应也进行了ParameterMapping里面的赋值
        String parse = genericTokenParser.parse(sql);

        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
        BoundSql boundSql = new BoundSql(parse, parameterMappings);
        return boundSql;
    }

    private PreparedStatement getPreparedStatement(Connection connection, MappedStatement mappedStatement, Object... params) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        // 获取sql语句，进行转换
        String sql = mappedStatement.getSql();

        // 进行sql解析，对应参数解析
        BoundSql boundSql = getBoundedSql(sql);

        // 预编译对象
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
        // 设值参数
        String paramterType = mappedStatement.getParamterType(); // 获取参数类型
        if (paramterType != null) {
            Class<?> parameterclassType = getClassType(paramterType);
            List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
            setObject(parameterMappingList, parameterclassType, params, preparedStatement);
        }
        return preparedStatement;
    }

    private void setObject(List<ParameterMapping> parameterMappingList, Class<?> parameterclassType, Object[] params, PreparedStatement preparedStatement) throws NoSuchFieldException, IllegalAccessException, SQLException {
        // parameterMappingList 可能传入的参数仅仅是基本类型，或者单个对象，或者集合对象
        // 传入的参数可能基于注解，或者map，或者对象
        for (int i = 0; i < parameterMappingList.size(); i++) {
            if (params.length > 1) {
                preparedStatement.setObject(i + 1, params[i]);
            } else if (params.length == 1 && params[0].getClass().getClassLoader() == null) {
                preparedStatement.setObject(i + 1, params[0]);
            } else {
                ParameterMapping parameterMapping = parameterMappingList.get(i);
                String content = parameterMapping.getContent();

                // 基于反射，找到对应参数类型的实体的值
                Field declaredField = parameterclassType.getDeclaredField(content);
                declaredField.setAccessible(true);
                Object o = declaredField.get(params[0]); // 可变参数,传入的只有一个对象

                // 对应下标，对应值
                preparedStatement.setObject(i + 1, o);
            }
        }
    }

}
